package com.bjkcst.hcnet;

import android.graphics.SurfaceTexture;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;

import com.hcnetsdk.jna.HCNetSDKJNAInstance;
import com.hikvision.netsdk.HCNetSDK;
import com.hikvision.netsdk.INT_PTR;
import com.hikvision.netsdk.NET_DVR_DEVICEINFO_V30;
import com.hikvision.netsdk.NET_DVR_PREVIEWINFO_V20;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;

/**
 * @author: glc.
 * Date: 2022/5/13  14:40
 * Description:
 */
public class CameraManager {
    private static final String TAG = "CameraManager";

    private volatile static CameraManager mInstance;

    /** 所有的摄像头设备
     * key => deviceNo{@link CameraBean#deviceNo}
     * value -> CameraBean 实体
     * */
    private static final Map<String, CameraBean> mCameraList = new HashMap<>();
    /** 当前预览的设备*/
    private List<CameraBean> currentPreviewCamera = new ArrayList<>();

    private static final int NET_DVR_RECONNECT_TIME = 2000;

    private CameraManager(){}

    /**
     * 获取 CameraManager 对象
     * @return CamearManager
     */
    public static CameraManager getInstance(){
        if (mInstance == null){
            synchronized (CameraManager.class){
                if (mInstance == null){
                    mInstance = new CameraManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化 SDK
     */
    public void initHCNetSDK(){
        HCNetSDK.getInstance().NET_DVR_Init();
        HCNetSDK.getInstance().NET_DVR_SetReconnect(NET_DVR_RECONNECT_TIME,true);
    }

    /**
     * 资源回收
     */
    public void recycle(){
        HCNetSDK.getInstance().NET_DVR_Cleanup();
    }


    /**
     * 设置待预览设备信息
     * @param cameraList
     */
    public void setCameraList(List<CameraBean> cameraList){
        for (CameraBean cameraBean : cameraList) {
            String deviceNoStr = String.valueOf(cameraBean.deviceNo);
            mCameraList.put(deviceNoStr,cameraBean);
        }
    }

    public void loginAllCameraDevices() throws InterruptedException{
        for (String deviceNo : mCameraList.keySet()) {
            CameraBean cameraBean = mCameraList.get(deviceNo);
            int loginId = cameraLogin(cameraBean);
            cameraBean.loginId = loginId;
            String deviceNoStr = String.valueOf(cameraBean.deviceNo);

            cameraAutoPreview(deviceNoStr);

            if (loginId < 0){
                reCameraLogin(cameraBean);
            }

        }
    }

    /**
     * 相机设备登录
     * @param cameraBean 相机配置实体
     * @return >0 登录成功
     */
    private int cameraLogin(CameraBean cameraBean){
        if (TextUtils.isEmpty(cameraBean.ip) || TextUtils.isEmpty(cameraBean.password) ||
                TextUtils.isEmpty(cameraBean.account) || cameraBean.port == 0){
            throw new SecurityException("ip、port、password、account 参数异常");
        }
        NET_DVR_DEVICEINFO_V30 deviceInfo = new NET_DVR_DEVICEINFO_V30();
        int loginId = HCNetSDK.getInstance().NET_DVR_Login_V30(cameraBean.ip,cameraBean.port,
                cameraBean.account,cameraBean.password,deviceInfo);
        if (loginId < 0){
            int i = HCNetSDK.getInstance().NET_DVR_GetLastError();
            String errorMsg = HCNetSDK.getInstance().NET_DVR_GetErrorMsg(new INT_PTR());
            Log.i("glc", "cameraLogin: "+ i+"   errorMsg="+errorMsg);
        }
        return loginId < 0 ? -1 : loginId;
    }

    private Map<Integer,Disposable> reLoginDisposable = new HashMap<>();
    private void reCameraLogin(CameraBean cameraBean){
//        int reconnectPeriodTime = LocalIniUtil.getInstance().loadIntData("ReconnectPeriodTime");
        int reconnectPeriodTime = 0;
        Log.i(TAG, "reCameraLogin: "+reconnectPeriodTime);
        Disposable subscribe = Observable.interval(reconnectPeriodTime <= 0 ? 8000 : reconnectPeriodTime, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .subscribe(aLong -> {
                    Log.i(TAG, "reCameraLogin: relogin deviceNo = " + cameraBean.deviceNo);
                    int login = cameraLogin(cameraBean);
                    if (login > -1) {
                        int deviceNo = cameraBean.deviceNo;
                        Objects.requireNonNull(mCameraList.get(String.valueOf(deviceNo))).loginId = login;
                        cameraAutoPreview(String.valueOf(deviceNo));
                        Disposable disposable = reLoginDisposable.get(deviceNo);
                        if (disposable != null && !disposable.isDisposed()) {
                            disposable.dispose();
                            reLoginDisposable.remove(deviceNo);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        Log.i(TAG, "accept: "+ throwable.getMessage());
                    }
                });
        reLoginDisposable.put(cameraBean.deviceNo,subscribe);
    }

    /**
     * 停止正在预览的设备
     * @param iRealHandle
     * @return
     */
    private boolean stopSinglePreview(int iRealHandle){
        return HCNetSDK.getInstance().NET_DVR_StopRealPlay(iRealHandle);
    }

    /**
     * 开始单个设备的预览
     * @param iUserID
     * @param sound
     * @param surfaceTexture
     * @return
     * @throws InterruptedException
     */
    private int startSinglePreview(int iUserID, int sound, SurfaceTexture surfaceTexture) throws InterruptedException {
        if (iUserID < 0){
            return -1;
        }
        NET_DVR_PREVIEWINFO_V20 struPlayInfo = new NET_DVR_PREVIEWINFO_V20();
        struPlayInfo.lChannel =1;
        struPlayInfo.dwStreamType =1;
        struPlayInfo.hHwnd = new Surface(surfaceTexture);

        int iRet = HCNetSDK.getInstance().NET_DVR_RealPlay_V40(iUserID, struPlayInfo, null);
        if (iRet < 0){
            int i = HCNetSDK.getInstance().NET_DVR_GetLastError();
            String errorMsg = HCNetSDK.getInstance().NET_DVR_GetErrorMsg(new INT_PTR());
            Log.i("glc", "cameraLogin: "+ i+"   errorMsg="+errorMsg);
        }
        if (iRet < 0){
            return -1;
        }
        if (sound == 1) {
            Thread.sleep(1000);
            boolean bRet = HCNetSDKJNAInstance.getInstance().NET_DVR_OpenSound((short) iRet);
            if (bRet) {
                Log.e("", "NET_DVR_OpenSound Succ!");
            }
        }

        return iRet;
    }


    private void cameraAutoPreview(String deviceNo)throws InterruptedException{
        CameraBean cameraBean = mCameraList.get(deviceNo);
        int loginId = cameraBean.loginId;
        if (loginId > -1) {
            int iRealHandle = startSinglePreview(loginId,cameraBean.sound,cameraBean.surfaceTexture);
            cameraBean.previewHandle = iRealHandle;
            cameraBean.uerID = loginId;
            cameraBean.playing = true;
        }
    }



    /**
     * 摄像头预览
     * @param deviceNo 预览设备的deivceNo {@link CameraBean#deviceNo}
     * @throws InterruptedException
     */
    public void previewDeivce(String ... deviceNo) throws InterruptedException {
        currentPreviewCamera.clear();
        for (String n : deviceNo) {
            CameraBean cameraBean = mCameraList.get(n);
            int loginId = cameraBean.loginId;
            if (loginId > -1) {
                int iRealHandle = startSinglePreview(loginId, cameraBean.sound, cameraBean.surfaceTexture);
                cameraBean.previewHandle = iRealHandle;
                cameraBean.uerID = loginId;
                cameraBean.playing = true;

                currentPreviewCamera.add(cameraBean);
            }else {

            }
        }
    }


    /**
     * 通过 deviceNo 获取到当前设备
     * @param deviceNo 设备编号
     * @return
     */
    public CameraBean getCameraBeanByDeviceNo(String deviceNo){
        return mCameraList.get(deviceNo);
    }

    /**
     * 切换预览设备
     */
    public void switchPreview() {
        for (CameraBean cameraBean : currentPreviewCamera) {
            int previewHandle = cameraBean.previewHandle;
            stopSinglePreview(previewHandle);
        }
    }
}
